package testplugin;

import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.papyrus.infra.core.listenerservice.IPapyrusListener;
import org.eclipse.papyrus.infra.core.services.ServiceException;
import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
import org.eclipse.papyrus.infra.core.utils.ServiceUtils;
import org.eclipse.papyrus.infra.core.utils.ServiceUtilsForActionHandlers;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.LiteralInteger;
import org.eclipse.uml2.uml.LiteralSpecification;
import org.eclipse.uml2.uml.LiteralUnlimitedNatural;
import org.eclipse.uml2.uml.Property;

import threadtest.VerificationRunnable;

import fr.inria.atlanmod.dummy.Mock;

public class PapyrusListener implements IPapyrusListener
{
	
	/**
	 * The job executing the verification
	 */
	private VerificationJob verificationJob;
	
	/**
	 * Just to see the difference with a job
	 */
	private Thread verificationThread;
	
	/**
	 *  The verification component 
	 */
	private Mock mock;

	private MarkersAndHighlightUtil mu;

	/**
	 * Initialize the verification component
	 */
	public PapyrusListener() 
	{
		mock = new Mock();
		mu = new MarkersAndHighlightUtil();
		
		
	}
	
	@Override
	public void notifyChanged(Notification arg0)
	{
		
		int eventType=arg0.getEventType();

		switch (eventType)
		{
			case Notification.ADD:
				processAdd(arg0);
				break;
				
			case Notification.SET:
				processSet(arg0);

			case Notification.REMOVE:
				processDelete(arg0);
				break;
			
			default:
				break;
		}
	}
	

	/**
	 * Verify when a element is added to the model
	 * @param arg0
	 */
	private void processAdd(Notification arg0)
	{
		IWorkbench wb = PlatformUI.getWorkbench();
		IWorkbenchWindow win = wb.getActiveWorkbenchWindow(); //Esto retorna null cuando lo pregunto desde un job
		IWorkbenchPage page = win.getActivePage();
		IWorkbenchPart workbenchPart= page.getActivePart();
		//System.out.println("El workbench que le paso: " + workbenchPart);
		mu.setWorkbenchPart(workbenchPart);
		
		
		
		try 
		{
			//New
			ServicesRegistry serviceRegistry = ServiceUtilsForActionHandlers.getInstance().getServiceRegistry();
			mu.setContainer(ServiceUtils.getInstance().getISashWindowsContainer(serviceRegistry));
			
			EObject newValue=(EObject) arg0.getNewValue();
			if(newValue instanceof org.eclipse.uml2.uml.Class || newValue instanceof Association || newValue instanceof Generalization || newValue instanceof Constraint)
			{
				verifyModel(newValue);
			}
		} 
		catch (ServiceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		

	}
	
	private void processSet(Notification arg0)
	{
		EObject toVerify=null;
		EObject notifier=(EObject) arg0.getNotifier();
		
		if(notifier instanceof Association || notifier instanceof Generalization || notifier instanceof Constraint) //que tipos de cambios ve?
		{
			verifyModel(notifier);
		}
		
		//Un cambio en una cardinalidad de una asociacion 
		else if(notifier instanceof LiteralInteger || notifier instanceof LiteralUnlimitedNatural)
		{
			
			LiteralSpecification li=(LiteralSpecification)notifier;
			Element owner=li.getOwner();
			if(owner instanceof Property)
			{
				Property pOwner=(Property)owner;
				//System.out.println("Esto que sera " + pOwner.getAssociation());
				Association a=pOwner.getOwningAssociation();
				if(a==null)
				{
					a=pOwner.getAssociation();
				}
				toVerify=a;
				verifyModel(toVerify);
			}
			
		}
		
		
		
	}
	
	private void processDelete(Notification arg0)
	{
		Object oldValue=arg0.getOldValue();
		if(oldValue instanceof org.eclipse.uml2.uml.Class || oldValue instanceof Association || oldValue instanceof Generalization || oldValue instanceof Constraint)
		{
			//verifyModel(oldValue);
		}
	}
	
	

	/**
	 * If a verification is in progress then it is cancelled and a new verification is schedulled. 
	 * @param object The model element that trigers the verification
	 * @return The result of the model verification
	 */
	private void verifyModel(EObject object)
	{
		
		if(verificationJob!=null && verificationJob.getState()==Job.RUNNING)
		{
			verificationJob.cancel();
		}
		
		verificationJob= new VerificationJob("VerificationJob");
		//verificationJob.setMock(mock); //TODO could be retrieved by the job from the listener
		verificationJob.setModifiedObject(object);
		verificationJob.setPapyruslistener(this);
		verificationJob.schedule();
		
//		if(verificationThread!=null)
//		{
//			verificationThread.interrupt();
//			System.out.println(verificationThread.getState());
//			
//		}
//		VerificationRunnable vr= new VerificationRunnable();
//		vr.setMock(mock);
//		vr.setModifiedObject(object);
//		vr.setPapyruslistener(this);
//		
//		verificationThread =new Thread(vr);
//		verificationThread.start();
		
	}

	
	public void showMarkersAndHighlight(Object newValue)
	{
		mu.setObjectsToMarkAndHighlight(newValue);
		mu.run();
	}
	
	public Mock getMock() {
		return mock;
	}

	public void setMock(Mock mock) {
		this.mock = mock;
	}



}
